/*
 * Copyright 2016-2022 dromara.org.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package org.dromara.mendmix.gateway.filter.subhandler;

import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Objects;

import org.apache.commons.lang3.StringUtils;
import org.dromara.mendmix.cache.CacheExpires;
import org.dromara.mendmix.cache.CacheUtils;
import org.dromara.mendmix.common.CustomRequestHeaders;
import org.dromara.mendmix.common.GlobalConstants;
import org.dromara.mendmix.common.exception.MainErrorType;
import org.dromara.mendmix.common.model.ApiInfo;
import org.dromara.mendmix.common.model.WrapperResponse;
import org.dromara.mendmix.common.util.JsonUtils;
import org.dromara.mendmix.common.util.ResourceUtils;
import org.dromara.mendmix.gateway.GatewayConfigs;
import org.dromara.mendmix.gateway.api.SystemApi;
import org.dromara.mendmix.gateway.component.rewrite.RewriteExecutor;
import org.dromara.mendmix.gateway.component.rewrite.RewriteRule;
import org.dromara.mendmix.gateway.filter.SubFilterHandler;
import org.dromara.mendmix.gateway.helper.RequestContextHelper;
import org.dromara.mendmix.gateway.model.BizSystem;
import org.dromara.mendmix.gateway.model.BizSystemModule;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.http.server.reactive.ServerHttpRequest.Builder;
import org.springframework.web.server.ServerWebExchange;

/**
 * @description <br>
 * @author <a href="mailto:vakinge@gmail.com">jiangwei</a>
 * @date 2019年7月18日
 */
public class ResponseBodyRewriteHandler implements SubFilterHandler{

	private static Logger logger = LoggerFactory.getLogger("org.dromara.mendmix");
	
	private static final String REWRITE_RULE_CACHE_KEY = "RewriteRule:%s_%s";
	private static Map<String, Map<String,Integer>> codeConvertMappings = new HashMap<>();
	
	@Autowired
	private SystemApi systemApi;
	
	@Override
	public Object preHandle(ServerWebExchange exchange, BizSystemModule module, Builder requestBuilder) {
		return null;
	}

	@SuppressWarnings({ "rawtypes", "unchecked" })
	@Override
	public String postHandle(ServerWebExchange exchange, boolean successed, String responseBody) {
		if(!exchange.getResponse().getStatusCode().is2xxSuccessful()) {
			return null;
		}
		RewriteRule rewriteRule = currentRewriteRule(exchange);
		if(rewriteRule != null) {
			responseBody = RewriteExecutor.doRewrite(rewriteRule, responseBody);
		}
		//统一包装
		if(!GatewayConfigs.respRewriteEnabled) {
			return responseBody;
		}
		
		BizSystemModule module = RequestContextHelper.getCurrentModule(exchange);
		boolean keepResp = module.isStdResponse() || exchange.getResponse().getHeaders().containsKey(CustomRequestHeaders.HEADER_RESP_KEEP);
		if(keepResp) {
			return responseBody;
		}
		//
		if(StringUtils.isBlank(responseBody)) {
			final WrapperResponse<Object> blankResp = new WrapperResponse<>();
			return JsonUtils.toJson(blankResp);
		}
		
        int statusCode = exchange.getResponse().getStatusCode().value();
		
		String stdResponseBody = responseBody;
		String responseMsg = null;
		Object originData = null;
		boolean isStandardResp = false; //标准返回结构
        boolean rebuild = false;
		try {
			boolean isJsonObject = JsonUtils.isJsonObjectString(stdResponseBody);
			if(isJsonObject) {
				originData = JsonUtils.toHashMap(stdResponseBody);
			}else if(JsonUtils.isJsonArrayString(stdResponseBody)){
				originData = JsonUtils.toList(stdResponseBody, Map.class);
			}
			// 已经包含code结构不处理
			isStandardResp = isJsonObject
								&& ((Map)originData).containsKey(GlobalConstants.PARAM_CODE)
							    && (((Map)originData).size() == 1 
							    || ((Map)originData).containsKey(GlobalConstants.PARAM_DATA) 
							    || ((Map)originData).containsKey(GlobalConstants.PARAM_MSG) 
							   );
			if (!isStandardResp) {
				rebuild = !keepResp; //声明保持resp不处理
			}else{
				//code转换为标准格式
				final String routeName = module.getCurrentRouteName(exchange);
				if(codeConvertMappings.containsKey(routeName)) {
					rebuild = false;
					String oringinCode = Objects.toString(((Map)originData).get(GlobalConstants.PARAM_CODE), null);
					if(codeConvertMappings.get(routeName).containsKey(oringinCode)) {
						statusCode = codeConvertMappings.get(routeName).get(oringinCode);
					}else {
						statusCode = 500;
					}
					((Map)originData).put(GlobalConstants.PARAM_CODE, statusCode);
					if(RequestContextHelper.isDebugMode(exchange)) {
						logger.info(" codeConvert from:{} to:{}",oringinCode,statusCode);
					}
					stdResponseBody = JsonUtils.toJson(originData);
				}
			}

		} catch (Exception e) {
			String error = "Error during filtering[ResponseFilter]";
			logger.error(error, e);
			statusCode = 500;
			responseMsg = MainErrorType.SYSTEM_ERROR.getMessage();
		} finally {
			// 系统异常全部正常返回
			if (statusCode == 500) {
				exchange.getResponse().setStatusCode(HttpStatus.OK);
			}
			//
			if (rebuild) {
				Map<String, Object> rebuildObject;
				if(isStandardResp) {
					rebuildObject = (Map<String, Object>) originData;
				}else {
					rebuildObject = new LinkedHashMap<>(4);
					rebuildObject.put(GlobalConstants.PARAM_CODE, statusCode == 200 ? 200 : 500);
					if (StringUtils.isNotBlank(responseMsg))
						rebuildObject.put(GlobalConstants.PARAM_MSG, responseMsg);
					if (originData != null) {
						rebuildObject.put(GlobalConstants.PARAM_DATA, originData);
					} else if (StringUtils.isNotBlank(stdResponseBody)) {
						if(JsonUtils.isJsonArrayString(stdResponseBody)) {
							rebuildObject.put(GlobalConstants.PARAM_DATA, JsonUtils.toList(stdResponseBody, Map.class));
						}else if(JsonUtils.isJsonObjectString(stdResponseBody)) {
							rebuildObject.put(GlobalConstants.PARAM_DATA, JsonUtils.toHashMap(stdResponseBody));
						}else {
							rebuildObject.put(GlobalConstants.PARAM_DATA, stdResponseBody);
						}
					}
				}
				stdResponseBody = JsonUtils.toJson(rebuildObject);
			}
		}
		return stdResponseBody;
	}

	@Override
	public int ordered() {
		return 30;
	}
	
	@Override
	public void onStarted() {
		Map<String, String> routeCodeMapping = ResourceUtils.getMappingValues("filter.response.rewrite.codeMapping");
		routeCodeMapping.forEach((k,v)->{
			Map<String,Integer> _mapping = new HashMap<>();
			String[] parts = StringUtils.split(v, ";,:");
			for (int i = 0; i < parts.length; i+=2) {
				if(i < parts.length)_mapping.put(parts[i], Integer.parseInt(parts[i + 1]));
			}
			codeConvertMappings.put(k, _mapping);
		});
		logger.info("codeConvertMappings:{}",codeConvertMappings);
	}

	private RewriteRule currentRewriteRule(ServerWebExchange exchange) {
		String clientId = exchange.getRequest().getHeaders().getFirst(CustomRequestHeaders.HEADER_OPEN_APP_ID);
		if (StringUtils.isBlank(clientId)) {
			return null;
		}
		BizSystem system = RequestContextHelper.getCurrentSystem(exchange);
		ApiInfo api = RequestContextHelper.getCurrentApi(exchange);
		if(system.getId() == null || api == null)return null;
		String cacheKey = String.format(REWRITE_RULE_CACHE_KEY, clientId, api.getId());
		return CacheUtils.queryTryLevel1Cache(cacheKey, ()->systemApi.getRespRewriteRule(clientId, api.getId()), CacheExpires.IN_1DAY);
	} 

}
